/*
 * @Author: xiaosihan
 * @Date: 2021-11-28 14:33:08
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2023-12-19 19:59:01
 */

import utils from "@utils";
import elementResizeEvent from "element-resize-event";
import { round } from "lodash";
import { AmbientLight, AxesHelper, EventDispatcher, GridHelper, Group, Matrix4, PerspectiveCamera, PointLight, SRGBColorSpace, Scene, Vector2, Vector3, WebGLRenderer } from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import RingMesh from "./RingMesh";
import THREEMouse from "./THREEMouse";

type RingChatData = {
    name: string,
    value: number,
    height?: number,
    color?: string,
    onMousemove?: (e: MouseEvent) => void;
    onMouseleave?: (e: MouseEvent) => void;
    onMouseenter?: (e: MouseEvent) => void;
    onMousedown?: (e: MouseEvent) => void;
    onMouseup?: (e: MouseEvent) => void;
    onClick?: (e: MouseEvent) => void;
}

export default class RingChart3d extends EventDispatcher {

    constructor(div: HTMLDivElement, cameraDistance?: number) {
        super();
        this.init(div, cameraDistance);
        this.render();
    }

    // 渲染器
    renderer = (() => {
        const renderer = new WebGLRenderer({
            alpha: true,// 透明背景
            antialias: true, // 阴影抗锯齿
            precision: "highp", //precision:highp/mediump/lowp着色精度选择
            logarithmicDepthBuffer: true, // 对数深度缓冲区 避免当2个面完全重合时闪烁
            preserveDrawingBuffer: false, // 是否保存绘图缓冲
            stencil: false, // 模版
        });
        renderer.outputColorSpace = SRGBColorSpace;
        // | typeof NoColorSpace
        // | typeof SRGBColorSpace
        // | typeof LinearSRGBColorSpace
        // | typeof DisplayP3ColorSpace;
        return renderer;
    })();

    lableRender = document.createElement("canvas") as HTMLCanvasElement;

    lableRenderCtx = this.lableRender.getContext("2d")!;

    camera = new PerspectiveCamera(45, 1, 0.01, 2000);

    scene = new Scene();

    static tempVector = new Vector3();

    // y轴的正方向
    static YVector3 = new Vector3(0, 1, 0);

    // 相机的投影矩阵
    static viewProjectionMatrix = new Matrix4();

    // 对象的视图矩阵
    static viewMatrix = new Matrix4();

    //目前提供2种标签样式
    public lableType = 1 as 1 | 2 | 3;

    //渲染器的尺寸
    public size = new Vector2(0, 0);

    controls!: OrbitControls;

    // 是否首次渲染
    _isFristRender = true;

    // 鼠标事件对象
    threeMouse = new THREEMouse(this.renderer, this.camera, this.scene);
    /**
     * 初始化
     */
    init(div: HTMLDivElement, cameraDistance: number = 10) {
        div.innerHTML = "";
        div.append(this.renderer.domElement);

        div.append(this.lableRender);

        Object.assign(this.lableRender.style, {
            position: "absolute",
            left: "0px",
            top: "0px",
            zIndex: 1,
            pointerEvents: "none"
        });

        this.renderer.domElement.addEventListener("mousemove", () => {
            this.render();
        });

        elementResizeEvent(div, () => {
            let rect = window.getComputedStyle(div);
            let width = parseInt(rect.width);
            let height = parseInt(rect.height);
            this.resize(width, height);
        });

        let rect = window.getComputedStyle(div);
        let width = parseInt(rect.width);
        let height = parseInt(rect.height);
        this.resize(width, height);

        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.localClippingEnabled = true;
        this.camera.position.set(-10, 4, 0).setLength(cameraDistance);
        this.camera.lookAt(new Vector3(0, 0, 0));

        // 控制器
        // 非开发模式添加网格辅助线
        if (true && process.env.NODE_ENV === "development") {
            this.controls = new OrbitControls(this.camera, this.renderer.domElement);
            // this.controls.target.set(0, 1, 0);
            this.controls.enablePan = false;
            this.controls.addEventListener("change", () => this.render());
            // this.camera.lookAt(new Vector3(0, 1, 0));
        }

        this.ringMeshGroup.children = this.ringMeshs;

        this.scene.add(this.ringMeshGroup);



        // const pedestal = new Mesh(
        //     new CylinderBufferGeometry(3, 3, 0.1, 100, 1, false, 0, Math.PI * 2),
        //     new MeshLambertMaterial({ color: "#042778", transparent: true, opacity: 0.8 })
        // );

        // pedestal.rotateY(Math.PI / 2);
        // pedestal.position.set(0, -0.1, 0);

        // this.scene.add(pedestal);

        // 非开发模式添加网格辅助线
        if (process.env.NODE_ENV === "development") {
            const gridHelper = new GridHelper(10, 10, "#555", "#333");
            gridHelper.position.set(0, -0.1, 0);
            this.scene.add(gridHelper);
            this.scene.add(new AxesHelper(128));
        }

    }

    // 调整尺寸的方法
    resize(width: number, height: number) {
        this.camera.aspect = width / height;
        this.camera.updateProjectionMatrix();
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(width, height);
        this.size.set(width, height);
        this.lableRender.width = width;
        this.lableRender.height = height;
        this.render();
    }

    ringMeshs: Array<RingMesh> = [];

    ringMeshGroup = new Group();

    ambientLight = (() => {
        const ambientLight = new AmbientLight("#fff", 3);
        this.scene.add(ambientLight);
        return ambientLight;
    })();

    pointLight = (() => {
        const pointLight = new PointLight("#fff", 5);
        pointLight.decay = 0;
        pointLight.distance = 20;
        pointLight.position.set(0, 10, -10);
        this.scene.add(pointLight);
        return pointLight;
    })();

    pointLight1 = (() => {
        const pointLight1 = new PointLight("#fff", 5);
        pointLight1.decay = 0;
        pointLight1.distance = 20;
        pointLight1.position.set(0, 2, 7);
        this.scene.add(pointLight1);
        return pointLight1;
    })();


    _isRendering = false;

    render = () => {

        if (this._isRendering) {
            return;
        }

        if (this.ringMeshs.length === 0) {
            this._isRendering = false;
            return;
        } else if (this.ringMeshs.some(ringMesh => !ringMesh.transitionIsDone())) {
            this._isRendering = true;
            // 每50 毫秒执行一次
            setTimeout(() => {
                this._isRendering = false;
                this.render();
            }, 50);
        } else if (this._isFristRender) {
            this._isRendering = false;
            this._isFristRender = false;
            //@ts-ignore
            this.dispatchEvent({ type: "fristComplete" });
        }

        // 分发鼠标事件
        this.threeMouse.dispatchEvent(this.ringMeshs);
        // 渲染场景
        this.renderer.render(this.scene, this.camera);

        // 绘制标签
        this.lableType === 1 && this.renderLable1();
        this.lableType === 2 && this.renderLable2();
        this.lableType === 3 && this.renderLable3();
    }

    // 三维向量转化成屏幕上的2维向量
    v3tov2(v3: Vector3) {
        v3.project(this.camera);
        v3.x = ((v3.x + 1) * this.size.x * 0.5);
        v3.y = ((-v3.y + 1) * this.size.y * 0.5);
    }

    // 获取标签列表
    getLables() {
        const lable = this.ringMeshs.map(ringMesh => {
            const { name, color } = ringMesh.userData;
            return { name, color };
        });
        return lable;
    }

    // 标签样式1
    renderLable1() {

        const size = this.renderer.getSize(new Vector2);

        const widthHalf = size.x / 2;
        const heightHalf = size.y / 2;
        let tatolValue = 0; //总值

        this.ringMeshs.map(d => tatolValue += d.userData.value);

        this.lableRenderCtx.clearRect(0, 0, size.x, size.y);

        this.ringMeshs.map((ringMesh, i) => {

            const { startAngle, angle, height } = ringMesh._transitionParam.getJson();

            RingChart3d.viewMatrix.copy(this.camera.matrixWorldInverse);
            RingChart3d.viewProjectionMatrix.multiplyMatrices(this.camera.projectionMatrix, RingChart3d.viewMatrix);

            RingChart3d.tempVector.set(0, 1, 2.5).applyAxisAngle(RingChart3d.YVector3, (startAngle + (angle * 0.5)));

            this.v3tov2(RingChart3d.tempVector);

            const { x, y } = RingChart3d.tempVector;

            const { name, color, value } = ringMesh.userData;

            const position = new Vector2(x, y);

            const centerPosition = new Vector2(widthHalf, heightHalf);

            // 绘制直线
            this.lableRenderCtx.strokeStyle = color;
            this.lableRenderCtx.lineWidth = 2;
            this.lableRenderCtx.beginPath();

            // 线段的第一个起点
            this.lableRenderCtx.moveTo(position.x, position.y);

            // 线段的第二个点
            position.add(position.clone().sub(centerPosition).setLength(10));

            position.x += Math.sign(position.x - centerPosition.x) * 10;
            position.y -= 20;

            this.lableRenderCtx.lineTo(position.x, position.y);

            // 线段的第三个点
            position.x += (80 * ((position.x < widthHalf) ? -1 : 1));

            this.lableRenderCtx.lineTo(position.x, position.y);


            // 绘制文字的位置
            position.x += (5 * ((position.x < widthHalf) ? -1 : 1));

            this.lableRenderCtx.shadowOffsetX = 0;//用来设定阴影在 X轴的延伸距
            this.lableRenderCtx.shadowOffsetX = 0;//用来设定阴影在 Y轴的延伸距
            this.lableRenderCtx.shadowBlur = 0;//设定阴影的模糊程度 默认0

            // 绘制线
            this.lableRenderCtx.stroke();

            this.lableRenderCtx.shadowOffsetX = -1;//用来设定阴影在 X轴的延伸距
            this.lableRenderCtx.shadowOffsetX = -1;//用来设定阴影在 Y轴的延伸距
            this.lableRenderCtx.shadowBlur = 5;//设定阴影的模糊程度 默认0
            this.lableRenderCtx.shadowColor = "rgba(0, 0, 0, 1)";//设定阴影颜色效果

            this.lableRenderCtx.font = ["18px", "微软雅黑"].join(" ");

            this.lableRenderCtx.textAlign = "center";

            this.lableRenderCtx.fillStyle = "#fff";

            this.lableRenderCtx.textBaseline = "middle";

            this.lableRenderCtx.fillText(`${round(value / tatolValue * 100, 1)}%`, x, y + 20);

            position.x += Math.sign(position.x - centerPosition.x) * -40;

            this.lableRenderCtx.fillText(`${utils.numToUnit(round(value)).valueUnit}例`, position.x, position.y - 15);

            this.lableRenderCtx.fillText(name, position.x, position.y + 15);
        });
    }

    // 标签样式2
    renderLable2() {

        const size = this.renderer.getSize(new Vector2);

        const widthHalf = size.x / 2;
        const heightHalf = size.y / 2;
        let tatolValue = 0; //总值

        this.ringMeshs.map(d => tatolValue += d.userData.value);

        this.lableRenderCtx.clearRect(0, 0, size.x, size.y);

        this.ringMeshs.map((ringMesh, i) => {

            const { startAngle, angle, height } = ringMesh._transitionParam.getJson();

            RingChart3d.viewMatrix.copy(this.camera.matrixWorldInverse);
            RingChart3d.viewProjectionMatrix.multiplyMatrices(this.camera.projectionMatrix, RingChart3d.viewMatrix);

            RingChart3d.tempVector.set(0, 1, 2.5).applyAxisAngle(RingChart3d.YVector3, (startAngle + (angle * 0.5)));

            this.v3tov2(RingChart3d.tempVector);

            const { x, y } = RingChart3d.tempVector;

            const { name, color, value } = ringMesh.userData;

            const position = new Vector2(x, y);

            const centerPosition = new Vector2(widthHalf, heightHalf);

            // 绘制直线
            this.lableRenderCtx.strokeStyle = color;
            this.lableRenderCtx.lineWidth = 2;
            this.lableRenderCtx.beginPath();

            // 线段的第一个起点
            this.lableRenderCtx.moveTo(position.x, position.y += 15);

            // 线段的第二个点
            position.add(position.clone().sub(centerPosition).setLength(10));

            position.x += Math.sign(position.x - centerPosition.x) * 10;
            position.y -= 20;

            this.lableRenderCtx.lineTo(position.x, position.y);

            // 线段的第三个点
            position.x += (80 * ((position.x < widthHalf) ? -1 : 1));

            this.lableRenderCtx.lineTo(position.x, position.y);


            // 绘制文字的位置
            position.x += (5 * ((position.x < widthHalf) ? -1 : 1));

            this.lableRenderCtx.shadowOffsetX = 0;//用来设定阴影在 X轴的延伸距
            this.lableRenderCtx.shadowOffsetX = 0;//用来设定阴影在 Y轴的延伸距
            this.lableRenderCtx.shadowBlur = 0;//设定阴影的模糊程度 默认0

            // 绘制线
            this.lableRenderCtx.stroke();

            this.lableRenderCtx.shadowOffsetX = -1;//用来设定阴影在 X轴的延伸距
            this.lableRenderCtx.shadowOffsetX = -1;//用来设定阴影在 Y轴的延伸距
            this.lableRenderCtx.shadowBlur = 5;//设定阴影的模糊程度 默认0
            this.lableRenderCtx.shadowColor = "rgba(0, 0, 0, 1)";//设定阴影颜色效果

            this.lableRenderCtx.font = ["18px", "微软雅黑"].join(" ");

            this.lableRenderCtx.textAlign = "center";

            this.lableRenderCtx.fillStyle = "#fff";

            this.lableRenderCtx.textBaseline = "middle";

            position.x += Math.sign(position.x - centerPosition.x) * -40;

            this.lableRenderCtx.fillText(`${round(value / tatolValue * 100, 1)}%`, position.x, position.y - 15);

            this.lableRenderCtx.fillText(name, position.x, position.y + 15);
        });
    }

    // 标签样式3
    renderLable3() {

        const size = this.renderer.getSize(new Vector2);

        const widthHalf = size.x / 2;
        const heightHalf = size.y / 2;
        let tatolValue = 0; //总值

        this.ringMeshs.map(d => tatolValue += d.userData.value);

        this.lableRenderCtx.clearRect(0, 0, size.x, size.y);

        this.ringMeshs.map((ringMesh, i) => {

            const { startAngle, angle, height } = ringMesh._transitionParam.getJson();

            RingChart3d.viewMatrix.copy(this.camera.matrixWorldInverse);
            RingChart3d.viewProjectionMatrix.multiplyMatrices(this.camera.projectionMatrix, RingChart3d.viewMatrix);

            RingChart3d.tempVector.set(0, 1, 2.5).applyAxisAngle(RingChart3d.YVector3, (startAngle + (angle * 0.5)));

            this.v3tov2(RingChart3d.tempVector);

            const { x, y } = RingChart3d.tempVector;

            const { name, color, value } = ringMesh.userData;

            const position = new Vector2(x, y);

            const centerPosition = new Vector2(widthHalf, heightHalf);

            // 绘制直线
            this.lableRenderCtx.strokeStyle = "#ffffff";
            this.lableRenderCtx.lineWidth = 2;
            this.lableRenderCtx.beginPath();

            // 线段的第一个起点
            this.lableRenderCtx.moveTo(position.x, position.y += 15);

            // 线段的第二个点
            position.x += (20 * ((position.x < widthHalf) ? -1 : 1));

            this.lableRenderCtx.lineTo(position.x, position.y);

            // 线段的第个点
            position.add(position.clone().sub(centerPosition).setLength(10));
            position.x += Math.sign(position.x - centerPosition.x);
            position.y -= 20;

            this.lableRenderCtx.lineTo(position.x, position.y);

            // 绘制文字的位置
            position.x += (5 * ((position.x < widthHalf) ? -1 : 1));

            this.lableRenderCtx.shadowOffsetX = 0;//用来设定阴影在 X轴的延伸距
            this.lableRenderCtx.shadowOffsetX = 0;//用来设定阴影在 Y轴的延伸距
            this.lableRenderCtx.shadowBlur = 0;//设定阴影的模糊程度 默认0

            // 绘制线
            this.lableRenderCtx.stroke();

            this.lableRenderCtx.shadowOffsetX = -1;//用来设定阴影在 X轴的延伸距
            this.lableRenderCtx.shadowOffsetX = -1;//用来设定阴影在 Y轴的延伸距
            this.lableRenderCtx.shadowBlur = 5;//设定阴影的模糊程度 默认0
            this.lableRenderCtx.shadowColor = "rgba(0, 0, 0, 1)";//设定阴影颜色效果

            this.lableRenderCtx.font = ["18px", "微软雅黑"].join(" ");

            this.lableRenderCtx.textAlign = "center";

            this.lableRenderCtx.fillStyle = "#fff";

            this.lableRenderCtx.textBaseline = "middle";

            // position.x += Math.sign(position.x - centerPosition.x) * -40;

            this.lableRenderCtx.fillText(`${round(value / tatolValue * 100, 1)}%`, position.x, position.y - 15);

            // this.lableRenderCtx.fillText(name, position.x, position.y + 15);
        });
    }

    // 默认颜色值
    defaultColor = ["#287CE8", "#E79145", "#41E4DE", "#FFC24D", "#32B8F0"];

    // 设置数据
    setData(ringDatas: Array<RingChatData>) {

        let tatolValue = 0; //总值
        let accumulate = 0;  // 累计计数器
        let maxValue = Math.max(...ringDatas.map(d => d.value));

        ringDatas.map(d => tatolValue += d.value);

        ringDatas.map((d, i) => {
            const angle = d.value / tatolValue * (Math.PI * 2);
            const startAngle = accumulate / tatolValue * (Math.PI * 2);
            accumulate += d.value;

            let ringMesh = this.ringMeshs[i];

            if (!ringMesh) {
                ringMesh = new RingMesh();
                ringMesh._transitionParam.reset({
                    startAngle: this._isFristRender ? 0 : startAngle
                });
                this.ringMeshs.push(ringMesh);
            }

            ringMesh.userData = {
                ...d,
                height: d.height || (d.value / maxValue),
                color: d.color || this.defaultColor[i % ringDatas.length]
            };
            ringMesh.onMousemove = d.onMousemove;
            ringMesh.onMouseleave = d.onMouseleave;
            ringMesh.onMouseenter = d.onMouseenter;
            ringMesh.onMousedown = d.onMousedown;
            ringMesh.onMouseup = d.onMouseup;
            ringMesh.onClick = d.onClick;

            ringMesh.setParam({
                startAngle,
                angle,
                // height: Math.pow(d.height || (d.value / maxValue), 2) * 2,
                height: d.height || (d.value / maxValue),
                color: d.color || this.defaultColor[i % ringDatas.length]
            });
        });

        this.ringMeshs.length = ringDatas.length;

        this.render();
    }

    // 设置hover的样式
    setHoverIndex(index?: number) {
        this.ringMeshs.map((ringMesh, i) => {
            ringMesh.setScale(index === i ? 1.1 : 1);
        });
        this.render();
    }

    dispose() {
        this.render = () => { }
        this.renderer.forceContextLoss();
    }


}